home *** CD-ROM | disk | FTP | other *** search
- #include "MMPI.HPP"
- #include <string.h>
- #include <ctype.h>
- #include <alloc.h>
- #include <stdio.h>
-
- // Labels for ASCII test file
- char *aScale[NUM_SCALES] = {
- "Q", // ? Unanswered question
- "F", // F Validity
- "K", // K Correction
- "Hs", // Hs Hypochondriasis
- "D", // D Depression
- "Hy", // Hy Hysteria
- "Pd", // Pd Psychopathic deviant
- "MfM", // Mf Masculinity-femininity Male
- "MfF", // Mf Masculinity-femininity Female
- "Pa", // Pa Paranoia
- "Pt", // Pt Psychasthenia
- "Sc", // Sc Schizophrenia
- "Ma", // Ma Hypomania
- "Si", // Si Social introversion
- "L" // L Lie
- };
-
- //
- // MMPIQuestion definitions
- //
-
- MMPIQuestion::MMPIQuestion(char *str)
- {
- text = strdup(str);
- clearScales();
- response = 'Q';
- }
-
- MMPIQuestion::~MMPIQuestion()
- {
- if(text)
- free(text);
- text = NULL;
- }
-
- void MMPIQuestion::clearScales()
- {
- memset(scale, ' ', NUM_SCALES);
- }
-
- int MMPIQuestion::setScale(char *key)
- {
- int c;
- char *keyword;
- int keywordLen;
- char *scaleResponse;
-
- keyword=key;
-
- // Parse out keyword and setting
- while(*keyword && isspace(*keyword))
- ++keyword;
- scaleResponse = keyword;
- while(*scaleResponse && isalnum(*scaleResponse))
- ++scaleResponse;
- keywordLen = (int)(scaleResponse - keyword);
- while(*scaleResponse && isspace(*scaleResponse))
- ++scaleResponse;
- *scaleResponse = toupper(*scaleResponse);
-
- if((*scaleResponse != 'T') && (*scaleResponse != 'F'))
- return 0; // Not a valid scale response
-
- for(c=0;c<NUM_SCALES;++c) {
- if(!strnicmp(keyword,aScale[c],keywordLen)) {
- scale[c]=*scaleResponse;
- return 1;
- }
- }
- return 0; // Not a valid keyword definition
- }
-
- // Ask and AskCheat are defined in USER.CPP
- char MMPIQuestion::getScale(int which)
- {
- if( which >= 0 && which < NUM_SCALES )
- return scale[which];
- else
- return ' ';
- }
-
- MMPITotal MMPIQuestion::eval()
- {
- MMPITotal tot;
- int c;
-
- if(response == 'Q')
- tot.total[Q] = 1;
- else
- for(c=F;c<NUM_SCALES;++c)
- tot.total[c] = (scale[c]==response);
- return tot;
- }
-
- //
- // MMPITotal definitions
- //
-
- MMPITotal::MMPITotal()
- {
- clear();
- }
-
- void MMPITotal::clear()
- {
- memset(total, 0, NUM_SCALES * sizeof(short));
- }
-
- // Add results calculated by a question eval
- MMPITotal& operator+= (MMPITotal& tot, MMPITotal& amount)
- {
- int c;
-
- for(c=0;c<NUM_SCALES;++c)
- tot.total[c]+=amount.total[c];
-
- return tot;
- }
-
- // Returns values adjusted by K scale
- MMPITotal MMPITotal::getCorrection()
- {
- MMPITotal ret;
- int k = total[K];
-
- ret.total[Hs] = (k/2);
- ret.total[Pd] = ((k*4)/10);
- ret.total[Pt] = k;
- ret.total[Sc] = k;
- ret.total[Ma] = ((k*2)/10);
-
- return ret;
- }
-
- // Create a result scale for display
- MMPITotal MMPITotal::scale(int gender)
- {
- MMPITotal rslt;
-
- // Return values start at 0 (rock bottom) and proceed upward to 127
-
- rslt.total[L] = 36 + (total[L] *3);
- rslt.total[F] = 44 + (total[F] *2);
- rslt.total[K] = 26 + (total[K] *19 / 10);
- rslt.total[Ma] = 11 + (total[Ma] *25 / 10);
- rslt.total[Si] = 20 + (total[Si] *21 / 20);
- rslt.total[Pa] = 27 + (total[Pa] *29 / 10);
-
- rslt.total[Mf_M] = 12+ (total[Mf_M] * 19 / 10);
- rslt.total[Mf_F] = 126- (total[Mf_F] * 21 / 10); // Essentially inverted
-
- if (gender == MALE) {
- rslt.total[Hs] = 21 + (total[Hs] * 26 / 10);
- rslt.total[D] = 10 + (total[D] * 24 / 10);
- rslt.total[Hy] = 26 + (total[Hy] * 18 / 10);
- rslt.total[Pd] = 7 + (total[Pd] * 23 / 10);
- rslt.total[Pt] = 2 + (total[Pt] * 21 / 10);
- rslt.total[Sc] = 6 + (total[Sc] * 19 / 10);
- }
- else {
- rslt.total[Hs] = 23 + (total[Hs] * 21 / 10);
- rslt.total[D] = 13 + (total[D] * 19 / 10);
- rslt.total[Hy] = 16 + (total[Hy] * 18 / 10);
- rslt.total[Pd] = 5 + (total[Pd] * 24 / 10);
- rslt.total[Pt] = 9 + (total[Pt] * 16 / 10);
- rslt.total[Sc] = 8 + (total[Sc] * 15 / 10);
- }
-
- return rslt;
- }
-
-
- //
- // MMPITest definitions
- //
-
- MMPINode::MMPINode(MMPIQuestion *obj)
- {
- dat = obj;
- next = NULL;
- prev = NULL;
- }
-
- MMPIQuestion *MMPINode::unlink()
- {
- if(prev)
- prev->next = next;
- if(next)
- next->prev = prev;
- return dat;
- }
-
-
- MMPITest::MMPITest(char *file)
- {
- curr = head = tail = NULL;
- size = 0;
- results.clear();
- Scale.clear();
- Adjusted.clear();
- Correction.clear();
- gender = 0;
- total = 0;
- load(file);
- }
-
- MMPITest::~MMPITest()
- {
- flush();
- }
-
- MMPINode *MMPITest::find(MMPIQuestion *srch)
- {
- MMPINode *n;
-
- n=head;
- while(n) {
- if(n->dat == srch) {
- curr = n;
- return n;
- }
- n = n->next;
- }
-
- return NULL;
- }
-
- MMPINode *MMPITest::goPosn(int p)
- {
- MMPINode *n;
-
- n=head;
-
- while(n && p) {
- n=n->next;
- p--;
- }
- curr = n;
- return n;
- }
-
- MMPINode* MMPITest::add(MMPIQuestion *obj)
- {
- MMPINode* newNode;
-
- if(find(obj))
- return NULL;
-
- newNode = new MMPINode(obj);
- ++ size;
-
- if( !tail ) {
- head = tail = curr = newNode;
- }
- else {
- newNode->prev = tail;
- tail->next = newNode;
- tail = newNode;
- curr = newNode;
- }
- return newNode;
- }
-
- void MMPITest::del(MMPIQuestion *obj)
- {
- MMPINode* foundNode;
-
- if( foundNode == find(obj) ) {
- foundNode->unlink();
-
- if(foundNode == head)
- head = foundNode->next;
- if(foundNode == tail)
- tail = foundNode->prev;
- if(foundNode == curr)
- curr = (foundNode->next)? foundNode->next : tail;
- delete foundNode->dat;
- delete foundNode;
- -- size;
- }
- }
-
- MMPIQuestion* MMPITest::first()
- {
- curr = head;
- if(head)
- return head->dat;
- return NULL;
- }
-
- MMPIQuestion* MMPITest::last()
- {
- curr = tail;
- if(tail)
- return tail->dat;
- return NULL;
- }
-
- MMPIQuestion* MMPITest::at(int pos)
- {
- MMPINode *n;
-
- n = goPosn(pos);
- if(n) {
- curr = n;
- return n->dat;
- }
- return NULL;
- }
-
- MMPIQuestion* MMPITest::next()
- {
- if(curr->next) {
- curr = curr->next;
- return (curr->dat);
- }
- return NULL;
- }
-
- MMPIQuestion* MMPITest::prev()
- {
- if(curr->prev) {
- curr = curr->prev;
- return (curr->dat);
- }
- return NULL;
- }
-
- MMPIQuestion* MMPITest::current()
- {
- if(curr)
- return curr->dat;
- else
- return NULL;
- }
-
- void MMPITest::flush()
- {
- MMPINode *cObj = head;
- MMPINode *nObj;
-
- while(cObj) {
- nObj = cObj->next;
- delete cObj->dat;
- delete cObj;
- cObj = nObj;
- }
- curr = head = tail = NULL;
- size = 0;
- }
-
-
- // Evaluate test responses
- int MMPITest::eval()
- {
- MMPIQuestion *current;
-
- if(isNotComplete())
- return 0;
-
- results.clear();
- Correction.clear();
- Adjusted.clear();
- Scale.clear();
-
- current = first();
- while(current) {
- results += current->eval();
- current = next();
- }
-
- Correction = results.getCorrection();
- Adjusted = results;
- Adjusted += Correction;
- Scale = Adjusted.scale(gender);
- return 1;
- }
-
- int MMPITest::isEmpty()
- {
- return( head->dat->response == 'Q' );
- }
-
- MMPIQuestion* MMPITest::isNotComplete()
- {
- MMPIQuestion *current;
-
- if(isEmpty())
- return NULL;
-
- // Find the first unanswered question
- current = first();
- while( current ) {
- if( current->response == 'Q' )
- break;
- current = next();
- }
-
- return current;
- }
-
-
-
-
- //
- // Load file rules:
- //
- // Skip all lines beginning with a '#'
- // Question definition begins with an empty line.
- // Get first line of text
- // Concatenate next line if the current line ends with a space
- // ...
- // Scale definitions immediately follow text a definition
- // Format is: ScaleLabel(white space)T or F
- // Question defs continue to EOF
- //
- // (Examine the test file for examples.)
- //
- int MMPITest::load(char *file)
- {
- #define BufSize 256
- #define TextMax 1024
- enum LoadStates {
- SCANNING,
- GET_TEXT,
- APP_TEXT,
- GET_SCALE,
- };
-
- ifstream inf;
-
- inf.open(file, ios::in);
-
- if( inf ) {
- char buf[BufSize];
- char textBuf[TextMax];
- int state = SCANNING;
- int bufSize;
- MMPIQuestion *newQ;
-
- while(inf.getline(buf, BufSize)) {
- if (*buf == '#')
- continue;
- bufSize = strlen(buf);
- buf[BufSize - 1]='\0';
- switch(state) {
-
- case SCANNING:
- if(!bufSize)
- state = GET_TEXT;
- break;
-
- case GET_TEXT:
- strcpy(textBuf, buf);
- if(buf[bufSize - 1] == ' ')
- state = APP_TEXT;
- else {
- newQ = new MMPIQuestion( textBuf );
- add(newQ);
- state = GET_SCALE;
- }
- break;
-
- case APP_TEXT:
- strcat(textBuf, buf);
- if(buf[bufSize - 1] != ' ') {
- newQ = new MMPIQuestion( textBuf );
- add(newQ);
- state = GET_SCALE;
- }
- break;
-
-
- case GET_SCALE:
- if(! newQ->setScale(buf)) {
- if(bufSize)
- state = SCANNING;
- else
- state = GET_TEXT;
- }
- break;
- };
-
- }
- }
- else
- return 0;
- inf.close();
- clearAnswers();
- return 1;
- }
-
-
- void MMPITest::clearAnswers()
- {
- MMPIQuestion *current;
-
- current = first();
- while(current) {
- current->response = 'Q';
- current = next();
- }
- results.clear();
- Correction.clear();
- Scale.clear();
- Adjusted.clear();
- gender = 0;
- first();
- }
-
- //
- // Answer file rules:
- //
- // Skip all lines beginning with a '#'
- // First line: Test file name
- // Second line: Name
- // Third line: Sex Age
- // T/F answers to EOF
- //
-
-
- int MMPITest::saveAnswers(char *path)
- {
- MMPIQuestion *current;
- int answer;
- ofstream onf;
-
- onf.open(path, ios::out);
-
- if(onf) {
- onf << gender;
- current = first();
- while(onf && current) {
- switch(current->response) {
- case 'T':
- onf << 'T';
- break;
- case 'F':
- onf << 'F';
- break;
- default:
- onf << 'Q';
- break;
- }
- current = next();
- }
- }
- else
- // Unable to save file
- return 0;
- onf.close();
- return 1;
- }
-
- int MMPITest::loadAnswers(char *path)
- {
- MMPIQuestion *current;
- int answer;
- char resp;
- ifstream inf;
-
- inf.open(path, ios::in);
- if(inf) {
- clearAnswers();
- gender = inf.get();
- if(gender != 'M' && gender != 'F') {
- inf.close();
- return 0;
- }
-
- current = first();
- while(inf && current) {
- resp = inf.get();
- current->response = (resp != 'T' && resp != 'F') ? 'Q' : resp;
- current = next();
- }
- }
- else
- return 0;
-
- inf.close();
- return 1;
- }
-
- int MMPITest::ResultForm(char *fName)
- {
- if(!eval())
- return 0;
-
- FILE *fp;
- if( (fp = fopen(fName, "w")) == NULL )
- return 0;
-
- fprintf(fp," Test Summary\n");
- fprintf(fp,"------------------------------------------------\n");
- fprintf(fp," Q Question: %d\n", Adjusted.total[Q]);
- fprintf(fp," F Validity: %d\n", Adjusted.total[F]);
- fprintf(fp," K Correction: %d\n", Adjusted.total[K]);
- fprintf(fp," Hs+.5K Hypochondriasis, adjusted: %d\n", Adjusted.total[Hs]);
- fprintf(fp," D Depression: %d\n", Adjusted.total[D]);
- fprintf(fp," Hy Hysteria: %d\n", Adjusted.total[Hy]);
- fprintf(fp," Pd+.4K Psychopathic deviant, adjusted: %d\n", Adjusted.total[Pd]);
- fprintf(fp," Mf Masculinity-femininity: %d\n", Adjusted.total[(gender == MALE)?Mf_M:Mf_F]);
- fprintf(fp," Pa Paranoia: %d\n", Adjusted.total[Pa]);
- fprintf(fp," Pt+K Psychasthenia, adjusted: %d\n", Adjusted.total[Pt]);
- fprintf(fp," Sc+K Schizophrenia, adjusted: %d\n", Adjusted.total[Sc]);
- fprintf(fp," Ma+.2K Hypomania, adjusted: %d\n", Adjusted.total[Ma]);
- fprintf(fp," Si Social introversion: %d\n", Adjusted.total[Si]);
- fprintf(fp," L Lie scale: %d\n", Adjusted.total[L]);
- fprintf(fp," Hs Hypochondriasis, base: %d\n", results.total[Hs]);
- fprintf(fp," Pd Psychopathic deviant, base: %d\n", results.total[Pd]);
- fprintf(fp," Pt Psychasthenia, base: %d\n", results.total[Pt]);
- fprintf(fp," Sc Schizophrenia, base: %d\n", results.total[Sc]);
- fprintf(fp," Ma Hypomania, base: %d\n", results.total[Ma]);
- fprintf(fp,"------------------------------------------------\n\n");
- fprintf(fp," L F K 1 2 3 4 5 6 7 8 9 0\n");
-
- int line;
- char lineBuf[64];
- int ref;
- // Cross reference MMPITotal array to build chart
- static int chartXRef[]={
- L,
- F,
- K,
- Hs,
- D,
- Hy,
- Pd,
- Mf_M,
- Pa,
- Pt,
- Sc,
- Ma,
- Si
- };
- chartXRef[7] = (gender == MALE)?Mf_M:Mf_F;
- int val;
-
- for(line=24 ;line >= 4;line--) {
-
- for(ref=0;ref<50;++ref)
- lineBuf[ref]=' ';
-
- for(ref=0;ref<13;++ref) {
- val = Scale.total[chartXRef[ref]]/5;
- if(val == line) {
- lineBuf[ref*4]='x';
- }
- else
- if( (line == 24) && (val > line) )
- lineBuf[ref*4]='^';
- else
- if( (line == 4) && (val < line) )
- lineBuf[ref*4]='v';
- else
- lineBuf[ref*4]='.';
- }
-
- if((line==6)||(line==14))
- for(ref=0;ref<50;++ref)
- switch(lineBuf[ref])
- {
- case '.':
- lineBuf[ref]='-';
- break;
- case ' ':
- lineBuf[ref]='-';
- break;
- }
- lineBuf[50]='\0';
- fprintf( fp, "%4d %.49s %-4d\n", line*5, lineBuf, line*5 );
- }
- fprintf(fp," L F K Hs D Hy Pd Mf Pa Pt Sc Ma Si\n");
- fclose(fp);
- return(1);
- }